//var cubeRotation = 0.0;

class WebGLWorld {
	constructor(gl) {
		this.gl = gl;
		this.objects = [];
	}

	initScene() {
	  this.gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
	  this.gl.clearDepth(1.0);                 // Clear everything
	  this.gl.enable(this.gl.DEPTH_TEST);           // Enable depth testing
	  this.gl.depthFunc(this.gl.LEQUAL);            // Near things obscure far things

	  // Clear the canvas before we start drawing on it.

	  this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
	}

	addObject(object) {
		this.objects.push(object);
	}

	render(now) {
		var camX = Math.sin(now) * 20.0;
		var camZ = Math.cos(now) * 20.0 ;
		var view = [camX, 0.0, camZ];
		for(var obj of this.objects) {
			obj.render(view);
		}
	}
}

class WebGLContext {
	constructor(gl, url) {
		this.gl = gl;
		var enableTexture = (url != undefined);
		this.buffer = this.initBuffers(this.gl,enableTexture);
		if (enableTexture) {
			this.texture = this.loadTexture(this.gl, url);
		}
		else {
			this.texture = undefined;
		}
	}

	getBuffer() {
		return this.buffer;
	}

	getTexture() {
		return this.texture;
	}

	getTextureBuffer() {
		console.log("textureBuffer isn't implemented");
		return undefined;
	}

	getColorBuffer() {
		console.log("colorBuffer isn't implemented");
		return undefined;
	}

	getNormalBuffer() {
		console.log("normalBuffer isn't implemented");
		return undefined;
	}
	//
	// initBuffers
	//
	// Initialize the buffers we'll need. For this demo, we just
	// have one object -- a simple three-dimensional cube.
	//
	initBuffers(gl,enableTexture) {

	  // Create a buffer for the cube's vertex positions.

	  const positionBuffer = gl.createBuffer();

	  // Select the positionBuffer as the one to apply buffer
	  // operations to from here out.

	  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

	  // Now create an array of positions for the cube.

	  const positions = [
		// Front face
		-1.0, -1.0,  1.0,
		 1.0, -1.0,  1.0,
		 1.0,  1.0,  1.0,
		-1.0,  1.0,  1.0,

		// Back face
		-1.0, -1.0, -1.0,
		-1.0,  1.0, -1.0,
		 1.0,  1.0, -1.0,
		 1.0, -1.0, -1.0,

		// Top face
		-1.0,  1.0, -1.0,
		-1.0,  1.0,  1.0,
		 1.0,  1.0,  1.0,
		 1.0,  1.0, -1.0,

		// Bottom face
		-1.0, -1.0, -1.0,
		 1.0, -1.0, -1.0,
		 1.0, -1.0,  1.0,
		-1.0, -1.0,  1.0,

		// Right face
		 1.0, -1.0, -1.0,
		 1.0,  1.0, -1.0,
		 1.0,  1.0,  1.0,
		 1.0, -1.0,  1.0,

		// Left face
		-1.0, -1.0, -1.0,
		-1.0, -1.0,  1.0,
		-1.0,  1.0,  1.0,
		-1.0,  1.0, -1.0,
	  ];

	  // Now pass the list of positions into WebGL to build the
	  // shape. We do this by creating a Float32Array from the
	  // JavaScript array, then use it to fill the current buffer.

	  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

	  // Now set up the texture coordinates for the faces.

	  var textureCoordBuffer = this.getTextureBuffer();
	  var colorBuffer = this.getColorBuffer();;

	  // Build the element array buffer; this specifies the indices
	  // into the vertex arrays for each face's vertices.

	  const indexBuffer = gl.createBuffer();
	  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);

	  // This array defines each face as two triangles, using the
	  // indices into the vertex array to specify each triangle's
	  // position.

	  const indices = [
		0,  1,  2,      0,  2,  3,    // front
		4,  5,  6,      4,  6,  7,    // back
		8,  9,  10,     8,  10, 11,   // top
		12, 13, 14,     12, 14, 15,   // bottom
		16, 17, 18,     16, 18, 19,   // right
		20, 21, 22,     20, 22, 23,   // left
	  ];

	  // Now send the element array to GL

	  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
		  new Uint16Array(indices), gl.STATIC_DRAW);

	  return {
		position: positionBuffer,
		textureCoord: textureCoordBuffer,
		indices: indexBuffer,
		color:colorBuffer,
		normal:this.getNormalBuffer()
	  };
	}

	//
	// Initialize a texture and load an image.
	// When the image finished loading copy it into the texture.
	//
	loadTexture(gl, url) {
	  const texture = gl.createTexture();
	  gl.bindTexture(gl.TEXTURE_2D, texture);

	  // Because images have to be download over the internet
	  // they might take a moment until they are ready.
	  // Until then put a single pixel in the texture so we can
	  // use it immediately. When the image has finished downloading
	  // we'll update the texture with the contents of the image.
	  const level = 0;
	  const internalFormat = gl.RGBA;
	  const width = 1;
	  const height = 1;
	  const border = 0;
	  const srcFormat = gl.RGBA;
	  const srcType = gl.UNSIGNED_BYTE;
	  const pixel = new Uint8Array([0, 0, 255, 255]);  // opaque blue
	  gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
					width, height, border, srcFormat, srcType,
					pixel);
	  var self = this;
	  const image = new Image();
	  image.crossOrigin = "./";
	  image.onload = function() {
		gl.bindTexture(gl.TEXTURE_2D, texture);
		gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
					  srcFormat, srcType, image);

		// WebGL1 has different requirements for power of 2 images
		// vs non power of 2 images so check if the image is a
		// power of 2 in both dimensions.
		if (self.isPowerOf2(image.width) && self.isPowerOf2(image.height)) {
		   // Yes, it's a power of 2. Generate mips.
		   gl.generateMipmap(gl.TEXTURE_2D);
		} else {
		   // No, it's not a power of 2. Turn of mips and set
		   // wrapping to clamp to edge
		   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
		   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
		   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
		}
	  };
	  image.src = url;

	  return texture;
	}

	isPowerOf2(value) {
	  return (value & (value - 1)) == 0;
	}
}

class WebGLTextureContext extends WebGLContext {
	getTextureBuffer() {
	  var gl = this.gl;
	  var textureCoordBuffer = gl.createBuffer();
	  gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);

	  const textureCoordinates = [
		// Front
		0.0,  0.0,
		1.0,  0.0,
		1.0,  1.0,
		0.0,  1.0,
		// Back
		0.0,  0.0,
		1.0,  0.0,
		1.0,  1.0,
		0.0,  1.0,
		// Top
		0.0,  0.0,
		1.0,  0.0,
		1.0,  1.0,
		0.0,  1.0,
		// Bottom
		0.0,  0.0,
		1.0,  0.0,
		1.0,  1.0,
		0.0,  1.0,
		// Right
		0.0,  0.0,
		1.0,  0.0,
		1.0,  1.0,
		0.0,  1.0,
		// Left
		0.0,  0.0,
		1.0,  0.0,
		1.0,  1.0,
		0.0,  1.0,
	  ];

	  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),
						gl.STATIC_DRAW);
	  return textureCoordBuffer;
	}
}

class WebGLLightingContext extends WebGLContext {
	getNormalBuffer() {
		var gl = this.gl;
	    var normalsBuffer = gl.createBuffer();
	    gl.bindBuffer(gl.ARRAY_BUFFER, normalsBuffer);

		const normals = [
			0.0, 0.0, 1.0, //front
			0.0, 0.0, 1.0, //front
			0.0, 0.0, 1.0, //front
			0.0, 0.0, 1.0, //front
			0.0, 0.0, -1.0, //back
			0.0, 0.0, -1.0, //back
			0.0, 0.0, -1.0, //back
			0.0, 0.0, -1.0, //back
			0.0, 1.0, 0.0, //top
			0.0, 1.0, 0.0, //top
			0.0, 1.0, 0.0, //top
			0.0, 1.0, 0.0, //top
			0.0, -1.0, 0.0, //bottom
			0.0, -1.0, 0.0, //bottom
			0.0, -1.0, 0.0, //bottom
			0.0, -1.0, 0.0, //bottom
			1.0, 0.0, 0.0, //right
			1.0, 0.0, 0.0, //right
			1.0, 0.0, 0.0, //right
			1.0, 0.0, 0.0, //right
			-1.0, 0.0, 0.0, //left
			-1.0, 0.0, 0.0, //left
			-1.0, 0.0, 0.0, //left
			-1.0, 0.0, 0.0, //left
		];

	  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals),
						gl.STATIC_DRAW);
	  return normalsBuffer;
	}
}

class WebGLColorContext extends WebGLContext {

	getColorBuffer() {
	  // Now set up the colors for the faces. We'll use solid colors
	  // for each face.
	  var gl = this.gl;

	  const faceColors = [
		[1.0,  1.0,  1.0,  1.0],    // Front face: white
		[1.0,  1.0,  1.0,  1.0],    // Front face: white
		[1.0,  1.0,  1.0,  1.0],    // Front face: white
		[1.0,  1.0,  1.0,  1.0],    // Front face: white
		[1.0,  1.0,  1.0,  1.0],    // Front face: white
		[1.0,  1.0,  1.0,  1.0],    // Front face: white
		//[1.0,  0.0,  0.0,  1.0],    // Back face: red
		//[0.0,  1.0,  0.0,  1.0],    // Top face: green
		//[0.0,  0.0,  1.0,  1.0],    // Bottom face: blue
		//[1.0,  1.0,  0.0,  1.0],    // Right face: yellow
		//[1.0,  0.0,  1.0,  1.0],    // Left face: purple
	  ];

	  // Convert the array of colors into a table for all the vertices.

	  var colors = [];

	  for (var j = 0; j < faceColors.length; ++j) {
		const c = faceColors[j];

		// Repeat each color four times for the four vertices of the face
		colors = colors.concat(c, c, c, c);
	  }

	  var colorBuffer = gl.createBuffer();
	  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
	  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
	  return colorBuffer;
	}
}

class WebGLShader {
	constructor(gl, vsSource, fsSource) {
		this.gl = gl;
	    this.shaderProgram = this.initShaderProgram(gl, vsSource, fsSource);
	}

	setPosition(name, buffer) {
		var gl = this.gl;
		var index = gl.getAttribLocation(this.shaderProgram, name)
	  {
		const numComponents = 3;
		const type = gl.FLOAT;
		const normalize = false;
		const stride = 0;
		const offset = 0;
		gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
		gl.vertexAttribPointer(
			index,
			numComponents,
			type,
			normalize,
			stride,
			offset);
		gl.enableVertexAttribArray(index);
	  }
	}

	setNormals(name, buffer) {
	    var gl = this.gl;
	    var index = gl.getAttribLocation(this.shaderProgram, name)
		{
			const numComponents = 3;
			const type = gl.FLOAT;
			const normalize = false;
			const stride = 0;
			const offset = 0;
			gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
			gl.vertexAttribPointer(
				index,
				numComponents,
				type,
				normalize,
				stride,
				offset);
			gl.enableVertexAttribArray(index);
		}
	}

	setTextureCoord(name, buffer) {
	  var gl = this.gl;
	  var index = gl.getAttribLocation(this.shaderProgram, name)
	  // Tell WebGL how to pull out the texture coordinates from
	  // the texture coordinate buffer into the textureCoord attribute.
	  {
		const numComponents = 2;
		const type = gl.FLOAT;
		const normalize = false;
		const stride = 0;
		const offset = 0;
		gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
		gl.vertexAttribPointer(
			index,
			numComponents,
			type,
			normalize,
			stride,
			offset);
		gl.enableVertexAttribArray(index);
	  }
	}

	setVertexColor(name, buffer) {
	  var gl = this.gl;
	  var index = gl.getAttribLocation(this.shaderProgram, name)
	  // Tell WebGL how to pull out the colors from the color buffer
	  // into the vertexColor attribute.
	  if (index>=0) {
		const numComponents = 4;
		const type = gl.FLOAT;
		const normalize = false;
		const stride = 0;
		const offset = 0;
		gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
		gl.vertexAttribPointer(
			index,
			numComponents,
			type,
			normalize,
			stride,
			offset);
		gl.enableVertexAttribArray(
			index);
	  }

	}

	bindArrayIndex(buffer) {
	  var gl = this.gl;
	  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
	}

	setUniformMat4(name, m4) {
	  var gl = this.gl;
	  var index = gl.getUniformLocation(this.shaderProgram, name);
	  gl.uniformMatrix4fv(
		  index,
		  false,
		  m4);
	}

	setUniformFVec3(name, v3) {
	  var gl = this.gl;
	  var index = gl.getUniformLocation(this.shaderProgram, name);
	  gl.uniform3fv(index, v3);
	}

	useProgram() {
		this.gl.useProgram(this.shaderProgram);
	}

	setSampler(name, texture) {
	  var gl = this.gl;
	  var index = gl.getUniformLocation(this.shaderProgram, name);
	  // Specify the texture to map onto the faces.

	  // Tell WebGL we want to affect texture unit 0
	  gl.activeTexture(gl.TEXTURE0);

	  // Bind the texture to texture unit 0
	  gl.bindTexture(gl.TEXTURE_2D, texture);

	  // Tell the shader we bound the texture to texture unit 0
	  gl.uniform1i(index, 0);
	}

	initShaderProgram(gl, vsSource, fsSource) {
	  const vertexShader = this.loadShader(gl, gl.VERTEX_SHADER, vsSource);
	  const fragmentShader = this.loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

	  // Create the shader program

	  const shaderProgram = gl.createProgram();
	  gl.attachShader(shaderProgram, vertexShader);
	  gl.attachShader(shaderProgram, fragmentShader);
	  gl.linkProgram(shaderProgram);

	  // If creating the shader program failed, alert

	  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
		console.log('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
		return null;
	  }

	  return shaderProgram;
	}

	//
	// creates a shader of the given type, uploads the source and
	// compiles it.
	//
	loadShader(gl, type, source) {
	  const shader = gl.createShader(type);

	  // Send the source to the shader object

	  gl.shaderSource(shader, source);

	  // Compile the shader program

	  gl.compileShader(shader);

	  // See if it compiled successfully

	  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
		console.log('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
		gl.deleteShader(shader);
		return null;
	  }

	  return shader;
	}

}

class WebGLModel {
	constructor(gl) {
		this.gl = gl;
	}

	setViewPos(view) {
		this.viewPos = view;
	}

	drawScene(projectionMatrix ,modelViewMatrix ,viewMatrix, buffers, texture) {
		console.log("drawScene should be implemented");
	}

}

class WebGLTextureModel extends WebGLModel {
	constructor(gl) {
		super(gl);
		this.vsSource = `
			attribute vec4 aVertexPosition;
			attribute vec2 aTextureCoord;

			uniform mat4 uViewMatrix;
			uniform mat4 uModelViewMatrix;
			uniform mat4 uProjectionMatrix;

			varying highp vec2 vTextureCoord;

			void main(void) {
			  gl_Position = uProjectionMatrix * uViewMatrix * uModelViewMatrix * aVertexPosition;
			  vTextureCoord = aTextureCoord;
			}
		  `;

	  // Fragment shader program

	  this.fsSource = `
		varying highp vec2 vTextureCoord;

		uniform sampler2D uSampler;

		void main(void) {
		  gl_FragColor = texture2D(uSampler, vTextureCoord);
		}
	  `;
	  this.shader = new WebGLShader(gl, this.vsSource, this.fsSource);
	}

	drawScene(projectionMatrix ,modelViewMatrix ,viewMatrix, buffers, texture) {
		var gl = this.gl;
		this.shader.setPosition('aVertexPosition', buffers.position);
		this.shader.setTextureCoord('aTextureCoord', buffers.textureCoord);
		this.shader.bindArrayIndex(buffers.indices);
		this.shader.useProgram();
		this.shader.setUniformMat4('uProjectionMatrix',projectionMatrix);
		this.shader.setUniformMat4('uModelViewMatrix',modelViewMatrix);
		this.shader.setUniformMat4('uViewMatrix',viewMatrix);
		this.shader.setSampler('uSampler', texture);
		  {
			const vertexCount = 36;
			const type = gl.UNSIGNED_SHORT;
			const offset = 0;
			gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
	  }
	}
}

class WebGLColorModel extends WebGLModel {
	constructor(gl) {
		super(gl);
		this.vsSource = `
			attribute vec4 aVertexPosition;
			attribute vec4 aVertexColor;

			uniform mat4 uViewMatrix;
			uniform mat4 uModelViewMatrix;
			uniform mat4 uProjectionMatrix;

			varying lowp vec4 vColor;

			void main(void) {
			  gl_Position = uProjectionMatrix * uViewMatrix * uModelViewMatrix * aVertexPosition;
			  vColor = aVertexColor;
			}
		  `;

	  // Fragment shader program

	  this.fsSource = this.getFsSource();
	  this.shader = new WebGLShader(gl, this.vsSource, this.fsSource);
	}

	getFsSource() {
	  return `
		void main(void) {
		  gl_FragColor = vColor;
		}
	  `;
	}

	drawScene(projectionMatrix ,modelViewMatrix ,viewMatrix, buffers, texture) {
		var gl = this.gl;
		this.shader.setPosition('aVertexPosition', buffers.position);
		this.shader.setVertexColor('aVertexColor',buffers.color);
		this.shader.bindArrayIndex(buffers.indices);
		this.shader.useProgram();
		this.shader.setUniformMat4('uProjectionMatrix',projectionMatrix);
		this.shader.setUniformMat4('uModelViewMatrix',modelViewMatrix);
		this.shader.setUniformMat4('uViewMatrix',viewMatrix);
		{
			const vertexCount = 36;
			const type = gl.UNSIGNED_SHORT;
			const offset = 0;
			gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
	    }
	}
}

class WebGLLightModel extends WebGLColorModel {
	constructor(gl) {
		super(gl);
	}

	getFsSource() {
		return `
			void main(void) {
			  gl_FragColor = vec4(1.0);
			}
		  `;
	}
}

class WebGLLightingObjectModel extends WebGLModel {
	constructor(gl,lightPos) {
		super(gl);
		this.vsSource = `
			attribute vec4 aVertexPosition;
			attribute vec3 aNormal;

			uniform mat4 uViewMatrix;
			uniform mat4 uModelViewMatrix;
			uniform mat4 uProjectionMatrix;

			varying lowp vec3 vNormal;
			varying lowp vec3 vFragPos;

			void main(void) {
			  gl_Position = uProjectionMatrix * uViewMatrix * uModelViewMatrix * aVertexPosition;
		  	  vFragPos = vec3(uModelViewMatrix * aVertexPosition);
			  vNormal = aNormal;
			}
		  `;

	  // Fragment shader program

	  this.fsSource = `
		precision mediump float;
	    uniform vec3 objectColor;
		uniform vec3 lightColor;
		uniform vec3 lightPos;
		uniform vec3 viewPos;

		varying lowp vec3 vNormal;
		varying lowp vec3 vFragPos;

		void main(void) {
		  float ambientStrength = 0.1;
		  vec3 ambient = ambientStrength * lightColor;

		  vec3 viewDir = normalize(viewPos - vFragPos);

		  vec3 norm = normalize(vNormal);
		  vec3 lightDir = normalize(lightPos - vFragPos);

		  vec3 reflectDir = reflect(-lightDir, norm);

		  float spec = pow(max(dot(viewDir, reflectDir), 0.0), 8.0);
		  float specularStrength = 0.5;
		  vec3 specular = specularStrength * spec * lightColor;

		  float diff = max(dot(norm, lightDir), 0.0);
		  vec3 diffUse = diff * lightColor;

		  vec3 result = (ambient + diffUse + specular) * objectColor;
		  gl_FragColor = vec4(result, 1.0);
		}
	  `;
	  this.shader = new WebGLShader(gl, this.vsSource, this.fsSource);
	  this.lightPos = lightPos;
	}

	drawScene(projectionMatrix ,modelViewMatrix ,viewMatrix, buffers, texture) {
		var gl = this.gl;
		this.shader.setPosition('aVertexPosition', buffers.position);
		this.shader.setNormals('aNormal',buffers.normal);
		this.shader.bindArrayIndex(buffers.indices);
		this.shader.useProgram();
		this.shader.setUniformMat4('uProjectionMatrix',projectionMatrix);
		this.shader.setUniformMat4('uModelViewMatrix',modelViewMatrix);
		this.shader.setUniformMat4('uViewMatrix',viewMatrix);
		this.shader.setUniformFVec3('objectColor',[1.0, 0.5, 0.31]);
		this.shader.setUniformFVec3('lightColor',[1.0, 1.0, 1.0]);
		this.shader.setUniformFVec3('lightPos',this.lightPos);
		this.shader.setUniformFVec3('viewPos',this.viewPos);
    	{
			const vertexCount = 36;
			const type = gl.UNSIGNED_SHORT;
			const offset = 0;
			gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
	    }
	}
}

class WebGLObject {
	constructor(name, gl, model, context, position) {
		this.name = name;
		this.gl = gl;
		this.context = context;
		this.model = model;
		this.position = position;
	}

	render(view) {
		this.model.setViewPos(view);
		this.drawScene(this.gl,
			this.context.getBuffer(), this.context.getTexture(),
			this.position, view);
	}

	//
	// Draw the scene.
	//
	drawScene(gl, buffers, texture, position, view) {
	  // Create a perspective matrix, a special matrix that is
	  // used to simulate the distortion of perspective in a camera.
	  // Our field of view is 45 degrees, with a width/height
	  // ratio that matches the display size of the canvas
	  // and we only want to see objects between 0.1 units
	  // and 100 units away from the camera.

	  const fieldOfView = 45 * Math.PI / 180;   // in radians
	  const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
	  const zNear = 0.1;
	  const zFar = 100.0;
	  const projectionMatrix = mat4.create();

	  // note: glmatrix.js always has the first argument
	  // as the destination to receive the result.
	  mat4.perspective(projectionMatrix,
					   fieldOfView,
					   aspect,
					   zNear,
					   zFar);

	  // Set the drawing position to the "identity" point, which is
	  // the center of the scene.
	  const modelViewMatrix = mat4.create();

	  // Now move the drawing position a bit to where we want to
	  // start drawing the square.

	  mat4.translate(modelViewMatrix,     // destination matrix
					 modelViewMatrix,     // matrix to translate
					 position);  // amount to translate
	  //mat4.rotate(modelViewMatrix,  // destination matrix
				  //modelViewMatrix,  // matrix to rotate
				  //cubeRotation,     // amount to rotate in radians
				  //[0, 0, 1]);       // axis to rotate around (Z)
	  //mat4.rotate(modelViewMatrix,  // destination matrix
				  //modelViewMatrix,  // matrix to rotate
				  //cubeRotation * .7,// amount to rotate in radians
				  //[0, 0, 0]);       // axis to rotate around (X)

	  //define view matrix here
	  const viewMatrix = mat4.create();
	  //eye, center, up
	  mat4.lookAt(viewMatrix, view, [0.0, 0.0, 0.0], [0.0, 1.0, 0.0]);
	  //mat4.lookAt(viewMatrix, [-5.0, 5.0, 10.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0]);

	  this.model.drawScene(projectionMatrix ,modelViewMatrix ,viewMatrix, buffers, texture);

	  // Update the rotation for the next draw

	  //cubeRotation += deltaTime;
	}
};

main();

//
// Start here
//
function main() {
  const canvas = document.querySelector('#glcanvas');
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  const gl = canvas.getContext('webgl');

  // If we don't have a GL context, give up now

  if (!gl) {
    alert('Unable to initialize WebGL. Your browser or machine may not support it.');
    return;
  }

  // Vertex shader program

  
  var world = new WebGLWorld(gl);
  var textureModel = new WebGLTextureModel(gl); 
  var textureContext = new WebGLTextureContext(gl, './cubetexture.png');

  var lightingContext = new WebGLLightingContext(gl);
  var lightingModel = new WebGLLightingObjectModel(gl,[-0.0, 5.0, -10.0]);

  var colorContext = new WebGLColorContext(gl);
  var colorModel = new WebGLLightModel(gl);

  world.addObject(new WebGLObject("light", gl, colorModel, colorContext, [-0.0, 5.0, -10.0]));
  world.addObject(new WebGLObject("lightingObject", gl, lightingModel, lightingContext, [5.0, 5.0, -4.0]));
  world.addObject(new WebGLObject("obj3", gl, textureModel, textureContext, [5.0, -5.0, -10.0]));
  world.addObject(new WebGLObject("obj4", gl, textureModel, textureContext, [-10.0, -10.0, -15.0]));

  // Draw the scene repeatedly
  function render(now) {
    now *= 0.001;  // convert to seconds
	world.initScene(gl);
	world.render(now);

    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
}

